package com.googlecode.contests_repo.validation;

import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import com.googlecode.contests_repo.core.model.Contest;

/**
 * A validation helper class used to validate the structure of contests.
 * 
 * @author joro, petko
 * 
 */
public class ContestValidator extends ResourceValidator {
	
	//Probably use apache.commons for this, but I found that faster 
	private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
	private static final String TIMEZONE_STRING = "Zulu";

	/**
	 * Checks whether the given contest folder contains valid data for the
	 * contest. Uses contest-schema.json for the validation.
	 * 
	 * @param folderPath
	 *            the path to the folder resource to validate
	 * @return a list of all validation errors
	 */
	public synchronized List<String> validate(String folderPath) {
		List<String> errors = super.validate(folderPath);
		if (errors.size() == 1 && errors.get(0).contains("does not exist")) {
			return errors;
		}
		File folder = new File(folderPath);

		// order / score attributes validation
		ObjectMapper mapper = new ObjectMapper();
		int[] scores = null;
		String[] orders = null;
		String dateString = "";
		File contestFile = new File(folder, "contest.json");
		
		Contest contest = null;
		try {
			contest =  mapper.readValue(contestFile, Contest.class);
			scores = contest.getProblemScores();
			orders = contest.getProblemOrder();
			dateString = contest.getStartTime();
			
			System.out.println(dateString);
		} 
		//Swallow and add the message to the errors, also stop the validation
		catch (JsonMappingException e) {
			errors.add(e.getMessage());
			return errors;
		} catch (JsonParseException e) {
			errors.add(e.getMessage());
			return errors;
		} catch (IOException e) {
			errors.add(e.getMessage());
			return errors;
		}
		
		
		List<File> problemList = ValidationTools.getResourceSubfolders(folder);
		compareProblemOrderWithScores(errors, folder, problemList, orders,
				scores);

		// append contest problem errors
		ProblemValidator problemValidator = new ProblemValidator();
		for (File problem : problemList) {
			List<String> problemErrors = problemValidator.validate(problem
					.getAbsolutePath());
			errors.addAll(problemErrors);
		}

		// start time validation
		if (contestFile.exists() && !isDateStringValid(dateString)) {
			errors.add(String.format(
					"Folder %s: contest.json: malformatted start date time",
					folder.getAbsolutePath()));
		}

		totalFilesCount += problemList.size();
		
		//Ignore hidden files
		int expectedFilesCount = folder.listFiles(new FileFilter() {
					public boolean accept(File file) {
						return !file.isHidden();
					}
				}).length;
		
		if (expectedFilesCount != totalFilesCount) {
			errors.add(String.format("Folder %s contains superfluous files.",
					folder));
		}

		return errors;
	}

	protected boolean jsonExists(List<String> errors, File folder,
			List<File> fileList) {
		File contestJasonFile = new File(folder, "contest.json");
		if (!fileList.contains(contestJasonFile)) {
			errors.add(String
					.format("Folder %s does not contain a \"contest.json\" file definition.",
							folder.getAbsolutePath()));
			return false;
		} else {
			List<String> validationErrors = ValidationTools.validateJson(
					contestJasonFile, Contest.class);
			errors.addAll(validationErrors);
			return true;
		}
	}

	private boolean isDateStringValid(String dateString) {
		if (dateString == null) {
			return false;
		}

		DateFormat dateFormat = new SimpleDateFormat(DATE_TIME_PATTERN);
		dateFormat.setTimeZone(TimeZone.getTimeZone(TIMEZONE_STRING));
		
		try {
			dateFormat.parse(dateString);
		} catch (ParseException e) {
			return false;
		}

		return true;
	}

	private void compareProblemOrderWithScores(List<String> errors,
			File folder, List<File> problemList, String[] orders,
			int[] scores) {
		if (orders != null) {
			boolean foldersExist = ValidationTools.areFileNamesContained(
					Arrays.asList(orders), problemList);
			if (!foldersExist) {
				errors.add(String
						.format("Folder %s: The contest does not contain all problems mentioned in problem order.",
								folder.getAbsolutePath()));
			}

			if (scores != null) {
				if (orders.length != scores.length) {
					errors.add(String
							.format("Folder %s: contest.json: problemScores and problemOrder have different sizes.",
									folder.getAbsolutePath()));
				}
			}
		}
		if (scores != null && orders == null) {
			int problemCount = problemList.size();
			if (problemCount != scores.length) {
				errors.add(String
						.format("Folder %s: Number of scores does not match the number of contest problems.",
								folder.getAbsolutePath()));
			}
		}
	}
}
